Оглавление

  • Рынок заведений общественного питания Москвы
    • Загрузим данные и изучим общую информацию
      • Считаем данные из csv-файла в датафрейм и сохраняем в переменную data
      • Изучим общую информацию о датасете
    • Выполним предобработку данных
      • Проверим на наличие дубликатов
      • Изучим пропуски в данных
      • Создадим столбец street с названиями улиц из столбца с адресом
      • Создадим столбец is_24_7 с обозначением, что заведение работает ежедневно и круглосуточно
      • Создадим столбец distr_short с аббревиатурой для каждого района
    • Анализ данных
      • Проанализируем распределение заведений по категориям
      • Исследуем количество посадочных мест в заведениях по категориям
      • Рассмотрим и изобразим соотношение сетевых и несетевых заведений в датасете
      • Выясним, какие категории заведений чаще являются сетевыми
      • Сгруппируем данные по названиям заведений и найдем топ-15 популярных сетей в Москве (по количеству заведений)
      • Проанализируем количество заведений в разных районах Москвы
      • Проанализируем средние (медианные) рейтинги в разных категориях заведений
      • Построим фоновую картограмму со средним (медианным) рейтингом заведений каждого района, отобразим все заведения датасета на карте
      • Проанализируем улицы Москвы по количеству заведений
        • Найдем топ-15 улиц по количеству заведений
        • Построим график распределения количества заведений и их категорий по топ-15 улицам
        • Найдем улицы, на которых находится только один объект общепита
      • Проанализируем примерную стоимость заказа в рублях и её изменение в зависимости от расположения завдения
      • Общий вывод по разделу
    • Детализируем исследование: открытие кофейни
      • Особенности расположения
        • Выделим улицы СЗАО, где отсутствуют другие кофейни в новый датафрейм data_streets_0
        • Выделим улицы СЗАО с большой концентрацией несетевых кофеен (2 и более) в новый датафрейм data_streets_1
      • Время работы
        • Проверим data_streets_1 на наличие круглосуточных кофеен
      • Рейтинги
        • Проанализируем средние рейтинги в data_streets_1 (улицы с большой концентрацией несетевых кофеен)
        • Проанализируем средние рейтинги в data_streets_0 (улицы, где отсутствуют кофейни)
      • Цены на капучино
      • Общий вывод по разделу

Рынок заведений общественного питания Москвы¶

Описание проекта

Инвесторы из фонда «Shut Up and Take My Money» решили попробовать себя в новой области и открыть заведение общественного питания в Москве. Цель проекта: подготовить исследование рынка Москвы, найти интересные особенности и презентовать полученные результаты, которые в будущем помогут в выборе подходящего инвесторам места.

Описание данных

Нам доступен датасет с заведениями общественного питания Москвы, составленный на основе данных сервисов Яндекс Карты и Яндекс Бизнес на лето 2022 года. Информация, размещённая в сервисе Яндекс Бизнес, могла быть добавлена пользователями или найдена в общедоступных источниках. Она носит исключительно справочный характер.

Файл moscow_places.csv:

  • name — название заведения;
  • address — адрес заведения;
  • category — категория заведения, например «кафе», «пиццерия» или «кофейня»;
  • hours — информация о днях и часах работы;
  • lat — широта географической точки, в которой находится заведение;
  • lng — долгота географической точки, в которой находится заведение;
  • rating — рейтинг заведения по оценкам пользователей в Яндекс Картах (высшая оценка — 5.0);
  • price — категория цен в заведении, например «средние», «ниже среднего», «выше среднего» и так далее;
  • avg_bill — строка, которая хранит среднюю стоимость заказа в виде диапазона, например:
    • «Средний счёт: 1000–1500 ₽»;
    • «Цена чашки капучино: 130–220 ₽»;
    • «Цена бокала пива: 400–600 ₽». и так далее;
  • middle_avg_bill — число с оценкой среднего чека, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Средний счёт»:
    • Если в строке указан ценовой диапазон из двух значений, в столбец войдёт медиана этих двух значений.
    • Если в строке указано одно число — цена без диапазона, то в столбец войдёт это число.
    • Если значения нет или оно не начинается с подстроки «Средний счёт», то в столбец ничего не войдёт.
  • middle_coffee_cup — число с оценкой одной чашки капучино, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Цена одной чашки капучино»:
    • Если в строке указан ценовой диапазон из двух значений, в столбец войдёт медиана этих двух значений.
    • Если в строке указано одно число — цена без диапазона, то в столбец войдёт это число.
    • Если значения нет или оно не начинается с подстроки «Цена одной чашки капучино», то в столбец ничего не войдёт.
  • chain — число, выраженное 0 или 1, которое показывает, является ли заведение сетевым (для маленьких сетей могут встречаться ошибки):
    • 0 — заведение не является сетевым
    • 1 — заведение является сетевым
  • district — административный район, в котором находится заведение, например Центральный административный округ;
  • seats — количество посадочных мест.

Загрузим данные и изучим общую информацию¶

In [1]:
# импортируем библиотеки
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import re
import plotly.express as px
from plotly import graph_objects as go
import json
from folium import Map, Choropleth
from folium import GeoJsonTooltip
import os
from folium import Map, Marker
from folium.plugins import MarkerCluster
In [2]:
# убираем предупреждения
pd.options.mode.chained_assignment = None  # default='warn'
from matplotlib.axes._axes import _log as matplotlib_axes_logger
matplotlib_axes_logger.setLevel('ERROR')

# установим максимальное количество отображающихся столбцов
pd.set_option('display.max_columns', None)

# установим максимальную ширину отображающихся столбцов
pd.set_option('max_colwidth', 120)

# установим максимальное количество отображающихся элементов серии
np.set_printoptions(threshold=np.inf)

Считаем данные из csv-файла в датафрейм и сохраняем в переменную data¶

In [3]:
# считаем данные
try:
    data = pd.read_csv('/datasets/moscow_places.csv')
except:
    data = pd.read_csv('moscow_places.csv')
data.head(5)
Out[3]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats
0 WoWфли кафе Москва, улица Дыбенко, 7/1 Северный административный округ ежедневно, 10:00–22:00 55.878494 37.478860 5.0 NaN NaN NaN NaN 0 NaN
1 Четыре комнаты ресторан Москва, улица Дыбенко, 36, корп. 1 Северный административный округ ежедневно, 10:00–22:00 55.875801 37.484479 4.5 выше среднего Средний счёт:1500–1600 ₽ 1550.0 NaN 0 4.0
2 Хазри кафе Москва, Клязьминская улица, 15 Северный административный округ пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00–02:00 55.889146 37.525901 4.6 средние Средний счёт:от 1000 ₽ 1000.0 NaN 0 45.0
3 Dormouse Coffee Shop кофейня Москва, улица Маршала Федоренко, 12 Северный административный округ ежедневно, 09:00–22:00 55.881608 37.488860 5.0 NaN Цена чашки капучино:155–185 ₽ NaN 170.0 0 NaN
4 Иль Марко пиццерия Москва, Правобережная улица, 1Б Северный административный округ ежедневно, 10:00–22:00 55.881166 37.449357 5.0 средние Средний счёт:400–600 ₽ 500.0 NaN 1 148.0

Изучим общую информацию о датасете¶

In [4]:
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8406 entries, 0 to 8405
Data columns (total 14 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   name               8406 non-null   object 
 1   category           8406 non-null   object 
 2   address            8406 non-null   object 
 3   district           8406 non-null   object 
 4   hours              7870 non-null   object 
 5   lat                8406 non-null   float64
 6   lng                8406 non-null   float64
 7   rating             8406 non-null   float64
 8   price              3315 non-null   object 
 9   avg_bill           3816 non-null   object 
 10  middle_avg_bill    3149 non-null   float64
 11  middle_coffee_cup  535 non-null    float64
 12  chain              8406 non-null   int64  
 13  seats              4795 non-null   float64
dtypes: float64(6), int64(1), object(7)
memory usage: 919.5+ KB

Вывод

  • в датасете представлено 8406 заведений
  • в датасете имеютсмя столбцы как в числовом формате float64 и int, так и в текстовом формате object
  • столбцы price и average_bill хранят категориальные данные, поэтому данные в этих столбцах представлены в текстовом формате object.

Выполним предобработку данных¶

Проверим на наличие дубликатов¶

Проверим наличие дублирующихся строк.

In [5]:
data[data.duplicated()]
Out[5]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats

Строки-дубликаты (явные/полные дубликаты) отсутствуют.

Также проверим наличие неявных дубликатов в столбце name для сетевых заведений (для несетевых заведений сложно угадать, это неявный дубликат или совершенно другое заведение).

In [6]:
data.query('chain==1')['name'].sort_values().unique()
Out[6]:
array(['1-я Креветочная', '10 Идеальных Пицц', '18 Грамм', '4 Сезона',
       '7 Сэндвичей', '8 Вафель', '9 Bar Coffee', 'ABC Coffee Roasters',
       'Air Coffee', 'Americano Black Coffee & Food', 'Arabix',
       'Arcus Bar And Food', 'Asia Gourmet', 'BB&Burgers', 'BFL’S',
       'Bakery', 'Bfl’s', 'BigСуши', 'Black Star Burger', 'Bodrero',
       'Bon Lavash', 'Boston seafood & bar', 'Bowl Family',
       'Brasserie Lambic', 'Bro&n', 'Brooms', 'Burger Club',
       'Burger Heroes', 'Bổ', 'CHICKO', 'Cafe', 'Cafe inn',
       'Café de Paris', 'Camera Obscura', 'Camorra Pizza e Birra',
       'Campus', 'Cassette Cafe', 'Chicha San Chen', 'Cinnabon',
       'City life', 'CofeFest', 'Coffee Bean', 'Coffee Break',
       'Coffee Guru', 'Coffee Like', 'Coffee Moose', 'Coffee Music',
       'Coffee Party', 'Coffee Point', 'Coffee Way',
       'Coffee and the City', 'Coffee in', "CoffeeBar'17", 'CoffeeShots',
       'Coffeebrain', "Coffeekaldi's", 'Coffeeshop Company',
       'Coffeesphere', 'Coffprice', 'Cofix', 'Conversation',
       'Cosmic latte', 'Crop. coffee & smoothie bar',
       'Cvc Китайская кухня', 'Deli2Go', 'Deli2go', 'Delimarche',
       'Demi Coffee Shop', 'DimSum & Co', 'Dizengof99', 'Drive',
       'Drive Café', "Dunkin' Donuts", 'Dон Хулио', 'Easy Pizza',
       'Eat&Play', 'Eco шаурма', 'Eshak', 'Farш', 'Fibo Pasta & Ravioli',
       'Finch', 'Fit Appėtit', 'Flip', 'Florentini',
       'Food Low Cost Sushi', 'FoodBand.ru', 'Frank by Баста',
       'Frankie Pizza', 'Free&co', 'French Bakery',
       'French Bakery SeDelice', 'Fresh', 'Fry’d', 'Gagawa',
       'Gentleman Coffee', 'Georgian Garden', 'GlowSubs', 'Grao De Cafe',
       'Green V. A. I.', "Gump's", 'GøG', 'Halal food', 'Hatimaki',
       'Hite', 'Ho Chu Pho', 'Home', 'Hot Dog Bulldog', 'Hq! Coffee',
       'I Need Doner', 'I-cup', 'IL Патио', 'Ikigai', 'Il Letterato',
       'Il Pittore', 'Ipho Cafe', 'Istanbul', "J'pan", "Jeffrey's Coffee",
       "Jeffrey's Coffeeshop", 'Kafin', 'Karavan', 'Kaya Coffee Shop',
       'Kimpab', 'Kitchen', 'Korean Chick', 'Krispy Kreme', 'Kulinari',
       'Kuzina', 'Le Круассан', 'Leon', 'Les', 'Levin Bakehouse',
       'Life Food', 'MamaMai', "Manny's Burger", 'Marketplace',
       'Max Bakery', 'Menza', 'Milk&Beans', 'More Poke', 'Mátes',
       'Naturality', 'Navat', 'Ngon', 'Nicepricecafe', 'Noba coffee',
       'Nova bubble tea', 'Omg Coffee', 'One Price Coffee', 'One Special',
       'One&Double', 'Osteria Mario', 'Paul', 'Pho', 'Pho Bo', 'Pho City',
       'Pho Hanoi', 'Pho Ngon', 'Pho Oanh', 'Pho Street', 'Pho U',
       'Pho Viet', 'PhoBo', 'Pims', 'Pizza Express 24', 'Pizza Hut',
       'Plov.com', 'Ploveberry', 'Point 242', 'Poke house', 'Prime',
       'Prolunch', 'Raw to Go', 'Remy Kitchen Bakery', 'Ricers',
       'Right Habits', 'SUШi', 'Saperavi Cafe', 'Sattva',
       'Scrocchiarella', 'SeDelice', 'Shawarma VIP House', 'Shwarm',
       'Skuratov, coffee roasters', 'Soul in the Bowl', 'Sova Coffee',
       'Spoon&Dagger', 'Star Hit Cafe', 'Steak it easy', 'Sub Cafe',
       'Take and Wake', 'Tasty Thai', 'Temple Bar', 'The Best Burgers',
       'The Hummus', 'The Wild Bean Cafe', 'The wild bean cafe',
       'The Вареники', 'Tokpokki', 'Torro Grill', 'Tsomi', 'Tu Ton',
       'Udcкафе', 'VASILCHUKÍ Chaihona №1', 'Viet Ngon', 'Viet Quan',
       'Vintage', 'Vse-em', 'Vua Pho', 'Wave California Poke',
       'We Cidreria', "Well's Home Cafe", 'White Fox', 'White Fox Cafe',
       'Wild Bean', 'Wild Bean Cafe', 'Wild bean cafe', 'Winners',
       'Wok Pho Mi', 'Wаурма', 'You&coffee', 'ZAcoffee', 'Zafferano',
       'Zames', 'Zotman Pizza', 'Zамания', 'Zефир', 'Ё-ланч', 'Ёрш',
       'Авокадо', 'АвтоСпаСтудия', 'Азбука daily', 'Азербайджан', 'Айва',
       'Академия', 'Алло! Пицца', 'Алёнка', 'АндерСон', 'Андиамо',
       'Антонио', 'Апельсин', 'Арамье', 'Арарат', 'Ача-Чача',
       'БИБЛИОТЕКА Shisha Lounge', 'Базилик', 'Бакинский дворик', 'Баку',
       'Бамбл Кофе', 'Бар-ресторан Территория', 'БарБариста', 'Баракат',
       'Барбарис', 'Батони', 'Беседка', 'Бизнес-кафе', 'Бистро 24',
       'Бишкек', 'БлинБери', 'Блинная', 'Бобры и утки', 'Бота', 'Бранч',
       'Брусника', 'Булка', 'Булкер', 'Булочная № 5', 'Булошная',
       'Булошная № 1', 'Бульбяная', 'Бульвар', 'Бургер Кинг',
       'Буркина фасоль', 'Буханка', 'Буше', 'Быстрое питание',
       'Бюро Пиццы', 'Бёргер Стейк', 'В парке вкуснее', 'В своей тарелке',
       'Вай Мэ!', 'Ванильное небо', 'Вареничная № 1', 'Везувио', 'Венахи',
       'Веранда', 'Верона', 'Весна', 'Виват Пицца', 'Виктория', 'Виолино',
       'Вкус Востока', 'Вкус Дня', 'Вкус Индии', 'Вкус востока',
       'Вкус дня', 'Воккер', 'Волконский', 'Восток', 'Восточная кухня',
       'Вояж', 'Встреча', 'Выпечка', 'ВьетКафе', 'Вьетнамская кухня',
       'Вьетнамское кафе', 'Галерея вкуса', 'Гамбринус',
       'Гастро Бистро Шаверма-Братуха', 'Гастробар', 'Гедонист',
       'Генацвале', 'Гладиатор', 'Голубка', 'Гораздо', 'Городок',
       'Городское', 'Горячая выпечка', 'Грабли', 'Гранат', 'Гриль Хаус',
       'Гриль парк', 'Грузинская кухня', 'Груша', 'Гудман', 'Гурман',
       'Гурманика', 'Гурмэ Ланч', 'Да, еда', 'ДаблБи', 'Даблби',
       'Дагестанская лавка', 'Дайнинг Холл', 'Дастархан', 'Дворик',
       'Дежене', 'Джаганнат', 'Джонджоли', 'Диана', 'Диван', 'Додо Пицца',
       'Домашние обеды', 'Домашний вкус', 'Домашняя еда',
       "Домино'с Пицца", 'Донер', 'Донер Кебаб', 'Донер Хаус',
       'Донер в пите', 'Донер кебаб', 'Донер хаус', 'Дружба', 'Дымок',
       'Дядюшка ХО', 'Евразия', 'Еврокафе', 'Еда', 'Еда Greek',
       'Ели Сацебели', 'Есть Хинкали&Пить Вино', 'Жан-Жак', 'Жига Дрыга',
       'Жираф', 'За обе щёки', 'Зандукели', 'Зарафшон', 'Здрасте',
       'Зефир', 'Иди обниму', 'Изи Паб', 'Изюм', 'Иль Марко',
       'Илья Муромец', 'Империя', 'Империя Пиццы', 'Индийская точка',
       'Искра', 'Итальянский ресторан DaPino', 'КОФЕПОРТ', 'Кабуки',
       'Кабул', 'Как Дома', 'Как дома', 'Калина', 'Калитки', 'Кампус',
       'Капучино Кидс', 'Караван', 'Кариночка', 'Карло', 'Катюша',
       'Кафе бар', 'Кафе-Столовая', 'Кафе-бар', 'Кафе-пекарня',
       'Кафетерий', 'Кафетериус', 'Кафетеррия', 'Кафешка', 'Кахури',
       'Квартира 44', 'Кинг Авто', 'Кинобар', 'Кинто', 'Китайская кухня',
       'Китайский ресторан', 'Китчен', 'Клёво', 'Колбасофф', 'Ком 1989',
       'Кондитерская Олега Ильина', 'Кондитерская-кулинария Брусника',
       'Корчма Тарас Бульба', 'Кофе', 'Кофе & Moloko', 'Кофе Хауз',
       'Кофе пью', 'Кофе с собой', 'Кофе твой друг', 'КофеТИ',
       'КофеТун-СушиТун', 'Кофедей', 'Кофейник', 'Кофемания', 'Крепери',
       'Крошка Картошка', 'Крошка картошка', 'Кружка Паб', 'Кулинариум',
       'Кулинариум - кулинария, пироги, салаты', 'Кулинария',
       'Кулинарная лавка братьев Караваевых', 'Кухня Полли', 'Кушавель',
       'Кушай Город', 'Ла Гатта', 'ЛавашОК', 'Лагман', 'Лагман Хаус',
       'Лаззат', 'Лаки Сувлаки', 'Ламаджо', 'Ланч поинт', 'Лао Ли',
       'Легенда', 'Лента Онлайн', 'Лепим и Варим', 'Лепим и варим', 'Лес',
       'Летняя веранда', 'Лечо', 'Ли', 'Ливан Хаус', 'Лимонадница',
       'Линдфорс', 'Литературное кафе', 'Лоза', 'Луна', 'Луч',
       'Любовь и Сладости', 'Ля Фантази', 'М2 Органик', 'МСК Lounge',
       'Магбургер', 'Магнолия', 'Мадина', 'Маковка',
       'Маленькая пекарня Журавлевых', 'Малетон', 'Манас', 'Мангал',
       'Маргарита', 'Марков двор', 'Мархал', 'Масала Хаус', 'Матрёшка',
       'Маяк', 'Мельница', 'Меркато', 'Мимино', 'Миндаль', 'Мир шашлыков',
       'Мисада', 'Мишель', 'Мое кафе', 'Может, кофе?',
       'Монастырская трапеза', 'МореМоре', 'Моремания', 'МосПлов',
       'Моё кафе', 'Му-Му', 'Музейное кафе', 'Мюнгер', 'Мясо на углях',
       'Мясо&Паста', 'Мясо&Рыба', 'Мята Lounge', 'Нам', 'Натахтари',
       'Находка', 'Нейборс', 'Неслучайно 08 08', 'Нияма',
       'Нью-Йорк пицца и гриль', 'ОШ', 'Оазис', 'Обедов', 'Огонёк',
       'Одесса-мама', 'Оливка', 'Оля', 'Омореморе', 'Ором', 'Орхан',
       'Остров', 'Островок', 'Островок Суши', 'Очаг', 'Пан Запекан',
       'Папа Джонс', 'Парк', 'Парус', 'Пекарня', 'Пекарня Буханка',
       'Пекарня № 1', 'Пельменная', 'Перекрёсток', 'Печорин', 'Пивбар',
       'Пивной ресторан Пив&Ко', 'Пикник', 'Пион', 'Пипони', 'Пирог Хауз',
       'Питербургер', 'Пицца Паоло', 'Пицца и Канноли', 'Пицца на районе',
       'Пицца экспресс', 'Пиццаменто', 'Плов', 'Плов центр', 'Подсолнух',
       'Полянка', 'Поминальная трапеза', 'Пончики',
       'Пончики! Выпекаем на месте', 'Пороселло', 'Правда Кофе',
       'Правда кофе', 'Праймбиф бар', 'Прованс', 'Прогресс', 'Пронто',
       'Профессор Пуф', 'ПтиШу', 'Публика', 'Пшеница', 'Пян-се',
       'Пять звёзд', 'Работа', 'Радуга', 'Ракета',
       'Раковарня Иван Раковар', 'Раковарня Клешни и хвосты', 'Раковая',
       'Рамен Тен', 'Рамен-Клаб', 'Рандеву', 'Реберная № 1', 'Регистан',
       'Ренессанс', 'Ресторан Мацони', 'Роко Бэй — Мох и кофе', 'Роллофф',
       'Рома', 'Роса', 'Рубим Бургер', 'Руккола',
       'Рыбная мануфактура № 1', 'Рэдимэйд', 'Рябина', 'СССР', 'Садовод',
       'Сакура', 'Салют', 'Сам пришёл', 'Самарканд', 'Самарканд Сити',
       'Самса № 1', 'Сварня', 'Свежая выпечка', 'Свежъ',
       'Свидание на крыше', 'Север-Метрополь', 'Сезоны', 'Семейное кафе',
       'Семейный Очаг', 'Сеть поминальных залов', 'Сикварули', 'Синнабон',
       'Сириус', 'Сити Лайф', 'Сити Пицца', 'Сицилия', 'Сказка', 'Скалка',
       'Смак', 'Снеди Феди', 'Соль', 'Соседи', 'Стардогс', 'Старый Баку',
       'Старый Сычуань', 'Старый город', 'Старый дворик',
       'Стейк & Бургер', 'Столичный вкус', 'Столовая № 1',
       'Столовая-кафе Росинка', 'Страдивари', 'Сундук', 'Сусеки',
       'Суши Love', 'Суши Wok', 'Суши Сет', 'Суши Таун', 'Суши-Пицца 312',
       'СушиСтор', 'Сыроварня', 'Сыто Пьяно', 'Сытый гусь', 'Тандыр',
       'Тандыр № 1', 'Тануки', 'Тапчан', 'Тарелка', 'Татнефть кафе',
       'Ташир пицца', 'Тбилисо', 'Тбилисоба', 'Твой кофе', 'Теремок',
       'Тирольские пироги', 'Ткемали', 'Топ Chick', 'Точка', 'Трактир',
       'ТрапеZа', 'Траттория Венеция', 'Трдельникъ', 'Турецкая лавка',
       'Уголок', 'Удача', 'Узбечка', 'Уйгурский лагман', 'Урюк', 'Уют',
       'Фергана', 'Филин', 'Фортуна', 'Франклинс Бургер',
       'Французская пекарня', 'Фрателло', 'Фудмаркет', 'Халал', 'Халва',
       'Халва, Сеть Почтоматов', 'Халяль', 'Хан Кебаб', 'Харчевниковъ',
       'Хачапури', 'Хачапури и вино', 'Хаят', 'Хинкали',
       'Хинкали - Gали!', 'Хинкали и Вино', 'Хинкали-Gали!', 'Хинкальная',
       'Хинкальная Легенда', 'Хинкальная Экспресс', 'Хинкальная № 1',
       'Хинкальный дом', 'Хлеб & Co', 'Хлеб Насущный', 'Хлеб да Выпечка',
       'Хлеб и Вино', 'Хлеб насущный', 'Хлеб с маслом',
       'Хлеба&Зрелищ пиццерия', 'Хлебница', 'Хорошее место',
       'Хочу шашлык', 'Чабан Чуду', 'Чай', 'Чайка', 'Чайная',
       'Чайная высота', 'Чайхана', 'Чайхана 24', 'Чайхана Sabr',
       'Чайхана Азия', 'Чайхана Баракат', 'Чайхана Бишкек сити',
       'Чайхана Манас', 'Чайхана ОШ', 'Чайхана Самарканд',
       'Чайхана Ташкент', 'Чайхана Халаль', 'Чайхана Халва',
       'Чайхана Халяль', 'Чайхана Хан', 'Чайхана Элина', 'Чайхана халяль',
       'Чайхана-24', 'Чайхона', 'Чайхона Айва', 'Чайхона № 1',
       'Чайхона №1', 'Чаме-Чаме', 'Чао-пицца', 'Чебуреки Манты',
       'Чебуречная СССР', 'Чебуречная история', 'Черетто', 'Черетто Море',
       'Чинар', 'Чито-Ра', 'Чихо', 'Шаверма', 'Шаурма 24',
       'Шаурма в пите', 'Шаурма на углях', 'Шафран', 'Шашлык',
       'Шашлык Хаус', 'Шашлыки', 'ШашлыкоFF', 'Шашлычная № 1',
       'Шашлычный двор', 'Шашлычный дворик', 'Шашлычок', 'Швили',
       'Шеф Бургер', 'Шик Шашлык', 'Шоколадница', 'Шоти', 'Шпинат',
       'Штолле', 'Щепка', 'Эзо', 'Эль кафе', 'Юг', 'Южный дворик',
       'Юность', 'Юрта', 'Я люблю суши', 'Ян Примус', 'Яндекс Лавка',
       'Яндекс.Лавка', 'Японская кухня'], dtype=object)

В стобце name встречаются разные названия одной и той же сети:

  • 'Яндекс Лавка', 'Яндекс.Лавка'
  • 'Чайхона № 1', 'Чайхона №1'
  • 'Чайхана Халаль', 'Чайхана Халяль', 'Чайхана халяль'
  • "Домино'с Пицца", "Домино'с пицца", 'Доминос пицца'

Переименуем их на уникальное название.

In [7]:
data['name'] = data['name'].replace(['Яндекс Лавка', 'Яндекс.Лавка'], 'Яндекс.Лавка')
data['name'] = data['name'].replace(['Чайхона № 1', 'Чайхона №1'], 'Чайхона №1')
data['name'] = data['name'].replace(['Чайхана Халаль', 'Чайхана Халяль', 'Чайхана халяль'], 'Чайхана Халяль')
data['name'] = data['name'].replace(["Домино'с Пицца", "Домино'с пицца", "Доминос пицца"], "Домино'с Пицца")

Изучим пропуски в данных¶

In [8]:
for column in data.columns:
    if data[column].isna().sum()>0:
        print("Название столбца:", column)
        print("Количество пропусков:", data[column].isna().sum())
        print('----')
print("В остальных столбцах пропуски отсутствуют")
Название столбца: hours
Количество пропусков: 536
----
Название столбца: price
Количество пропусков: 5091
----
Название столбца: avg_bill
Количество пропусков: 4590
----
Название столбца: middle_avg_bill
Количество пропусков: 5257
----
Название столбца: middle_coffee_cup
Количество пропусков: 7871
----
Название столбца: seats
Количество пропусков: 3611
----
В остальных столбцах пропуски отсутствуют

Пропуски в столбцах hours, price, avg_bill и seats скорее всего обозначает отсутствие данных о времени работы заведения, поэтому заполним пропуски значением no_info.

In [9]:
# заполняем пропуски и nan в столбцах hours, price, avg_bill и seats значением 'no_info'
data[['hours', 'price', 'avg_bill', 'seats']] = data[['hours', 'price', 'avg_bill', 'seats']].fillna('no_info')

Пропуски в столбце middle_avg_bill могут означать, что столбец avg_bill не начинаетя с подстроки «Средний счёт», а пропуски в столбце middle_coffee_cup - что столбец avg_bill не начинаетя с подстроки «Цена одной чашки капучино». Если эта догадка верная, заменим пропуски значением N/A - not applicable.

In [10]:
# проверим условие для столбца middle_avg_bill
if data[~data['avg_bill'].str.startswith("Средний счёт")]['middle_avg_bill'].isna().sum() == data['middle_avg_bill'].isna().sum():
    print('Пропуски в столбце middle_avg_bill встречаются только в тех случаях, когда значения из столбца avg_bill не начинаются')
    print('с подстроки «Средний счёт». Пропуски заменили значением N\A.')
    
    # заменим пропуски на 'N\A'
    data['middle_avg_bill'] = data['middle_avg_bill'].fillna('N/A')
else:
    print('Догадка неверная.')
print('----')

# проверим условие для столбца middle_coffee_cup
if data[~data['avg_bill'].str.startswith("Цена одной чашки капучино")]['middle_coffee_cup'].isna().sum() == data['middle_coffee_cup'].isna().sum():
    print('Пропуски в столбце middle_coffee_cup встречаются только в тех случаях, когда значения из столбца avg_bill не начинаются')
    print('с подстроки «Цена одной чашки капучино». Пропуски заменили значением N\A.')
    
    # заменим пропуски на 'N\A'
    data['middle_coffee_cup'] = data['middle_coffee_cup'].fillna('N/A')
else:
    print('Догадка неверная.')
Пропуски в столбце middle_avg_bill встречаются только в тех случаях, когда значения из столбца avg_bill не начинаются
с подстроки «Средний счёт». Пропуски заменили значением N\A.
----
Пропуски в столбце middle_coffee_cup встречаются только в тех случаях, когда значения из столбца avg_bill не начинаются
с подстроки «Цена одной чашки капучино». Пропуски заменили значением N\A.

Создадим столбец street с названиями улиц из столбца с адресом¶

In [11]:
data['street'] = data['address'].apply(lambda x: x.split(',')[1].strip())
data[['street']]
Out[11]:
street
0 улица Дыбенко
1 улица Дыбенко
2 Клязьминская улица
3 улица Маршала Федоренко
4 Правобережная улица
... ...
8401 Профсоюзная улица
8402 Пролетарский проспект
8403 Люблинская улица
8404 Люблинская улица
8405 Россошанский проезд

8406 rows × 1 columns

Создадим столбец is_24_7 с обозначением, что заведение работает ежедневно и круглосуточно¶

In [12]:
data['is_24_7'] = data['hours'] == 'ежедневно, круглосуточно'
data[['is_24_7']]
Out[12]:
is_24_7
0 False
1 False
2 False
3 False
4 False
... ...
8401 False
8402 False
8403 True
8404 True
8405 True

8406 rows × 1 columns

Создадим столбец distr_short с аббревиатурой для каждого района¶

In [13]:
# Функция для преобразования значения в аббревиатуру
def get_initials(text):
    words = text.replace('-', ' ').split()
    initials = ""
    for word in words:
        initials += word[0].upper()
    return initials

# Создание столбца с аббревиатурами
data['distr_short'] = data['district'].apply(get_initials)
data['distr_short'].unique()
Out[13]:
array(['САО', 'СВАО', 'СЗАО', 'ЗАО', 'ЦАО', 'ВАО', 'ЮВАО', 'ЮАО', 'ЮЗАО'],
      dtype=object)

Вывод

Итоговый датасет после предобработки данных и с новыми столбцами выглядит следующим образом.

In [14]:
data
Out[14]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats street is_24_7 distr_short
0 WoWфли кафе Москва, улица Дыбенко, 7/1 Северный административный округ ежедневно, 10:00–22:00 55.878494 37.478860 5.0 no_info no_info N/A N/A 0 no_info улица Дыбенко False САО
1 Четыре комнаты ресторан Москва, улица Дыбенко, 36, корп. 1 Северный административный округ ежедневно, 10:00–22:00 55.875801 37.484479 4.5 выше среднего Средний счёт:1500–1600 ₽ 1550.0 N/A 0 4.0 улица Дыбенко False САО
2 Хазри кафе Москва, Клязьминская улица, 15 Северный административный округ пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00–02:00 55.889146 37.525901 4.6 средние Средний счёт:от 1000 ₽ 1000.0 N/A 0 45.0 Клязьминская улица False САО
3 Dormouse Coffee Shop кофейня Москва, улица Маршала Федоренко, 12 Северный административный округ ежедневно, 09:00–22:00 55.881608 37.488860 5.0 no_info Цена чашки капучино:155–185 ₽ N/A 170.0 0 no_info улица Маршала Федоренко False САО
4 Иль Марко пиццерия Москва, Правобережная улица, 1Б Северный административный округ ежедневно, 10:00–22:00 55.881166 37.449357 5.0 средние Средний счёт:400–600 ₽ 500.0 N/A 1 148.0 Правобережная улица False САО
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
8401 Суши Мания кафе Москва, Профсоюзная улица, 56 Юго-Западный административный округ ежедневно, 09:00–02:00 55.670021 37.552480 4.4 no_info no_info N/A N/A 0 86.0 Профсоюзная улица False ЮЗАО
8402 Миславнес кафе Москва, Пролетарский проспект, 19, корп. 1 Южный административный округ ежедневно, 08:00–22:00 55.640875 37.656553 4.8 no_info no_info N/A N/A 0 150.0 Пролетарский проспект False ЮАО
8403 Самовар кафе Москва, Люблинская улица, 112А, стр. 1 Юго-Восточный административный округ ежедневно, круглосуточно 55.648859 37.743219 3.9 no_info Средний счёт:от 150 ₽ 150.0 N/A 0 150.0 Люблинская улица True ЮВАО
8404 Чайхана Sabr кафе Москва, Люблинская улица, 112А, стр. 1 Юго-Восточный административный округ ежедневно, круглосуточно 55.648849 37.743222 4.2 no_info no_info N/A N/A 1 150.0 Люблинская улица True ЮВАО
8405 Kebab Time кафе Москва, Россошанский проезд, 6 Южный административный округ ежедневно, круглосуточно 55.598229 37.604702 3.9 no_info no_info N/A N/A 0 12.0 Россошанский проезд True ЮАО

8406 rows × 17 columns

Анализ данных¶

Проанализируем распределение заведений по категориям¶

In [15]:
# Определяем порядок сортировки
sorted_data1 = data['category'].value_counts().sort_values(ascending=False).reset_index()
sorted_data1.columns = ['category', 'count']

# Задаем размер графика
plt.figure(figsize=(8, 6))

# Создаем график
graph1 = sns.barplot(x='category', y='count', data=sorted_data1)

# Поворачиваем подписи на 45 градусов
labels1 = graph1.get_xticklabels()
graph1.set_xticklabels(labels1, rotation=45)

# Добавляем заголовок и подписи осей
plt.title('Распределение заведений по категориям')
plt.xlabel('Категории заведений')
plt.ylabel('Количество')

# Отображаем график
plt.show()

Выводы

  • больше всего объектов общественного питания представлены в категории кафе, далее - рестораны
  • меньше всего встречается столовые и булочные.

Исследуем количество посадочных мест в заведениях по категориям¶

In [16]:
# Фильтруем только те заведения, где имеется информация о количестве стульев
sorted_data2 = data.query('seats != "no_info"')

# Сохранияем информацию о стульях в формате int
sorted_data2['seats'] = sorted_data2['seats'].astype(int)

# Определяем порядок сортировки
sorted_data2 = sorted_data2.groupby('category').agg({'seats': 'median'}).sort_values('seats', ascending=False).reset_index()

# Задаем размер графика
plt.figure(figsize=(8, 6))

# Создаем график
graph2 = sns.barplot(x='category', y='seats', data=sorted_data2)

# Поворачиваем подписи на 45 градусов
labels2 = graph2.get_xticklabels()
graph2.set_xticklabels(labels2, rotation=45)

# Добавляем значения над каждым столбцом
for index, row in sorted_data2.iterrows():
    graph2.annotate('{:.0f}'.format(row['seats']), xy=(index, row['seats']), ha='center', va='bottom')

# Добавляем заголовок и подписи осей
plt.title('Медианное количество посадочных мест в заведениях')
plt.xlabel('Категории заведений')
plt.ylabel('Медианное количество мест')

# Отображаем график
plt.show()

Выводы

  • самыми крупными заведениями по медианному количеству посадодчных мест являются рестораны, далее - бары/пабы и кофейни
  • меньше всего посадочных мест можно встретить в булочных, в среднем - 50 стульев (медианное значение)

Рассмотрим и изобразим соотношение сетевых и несетевых заведений в датасете¶

In [17]:
# готовим данные для графика
sorted_data3 = data['chain'].value_counts().sort_values(ascending=False).reset_index()
sorted_data3.columns = ['type', 'count']
sorted_data3
Out[17]:
type count
0 0 5201
1 1 3205
In [18]:
# готовим данные для графика
sorted_data3['type2'] = sorted_data3['type']
sorted_data3['type2'] = sorted_data3['type2'].replace([0, 1], ['несетевое', 'сетевое'])


# строим круговую диаграмму
fig3 = go.Figure(data=[go.Pie(labels=sorted_data3['type2'], # указываем значения, которые появятся на метках сегментов
                             values=sorted_data3['count'], # указываем данные, которые отобразятся на графике
                             pull = [0.1, 0])]) # добавляем аргумент, который выделит тип-лидер на графике
fig3.update_layout(title='Соотношение сетевых и несетевых заведений', # указываем заголовок графика
                  width=700, # указываем размеры графика
                  height=500,
                  annotations=[dict(x=1.12, # вручную настраиваем аннотацию легенды
                                    y=1.05,
                                    text='Количество заведений',
                                    showarrow=False)])
fig3.show() # выводим график

Вывод

  • несетевых заведений больше, они составляют 61.9% всех заведений.

Выясним, какие категории заведений чаще являются сетевыми¶

In [19]:
# Подготовим данные для графика
sorted_data4 = data.groupby(['category', 'chain']).agg({'name': 'count'}).reset_index()
sorted_data4['chain'] = sorted_data4['chain'].replace([0, 1], ['несетевое', 'сетевое'])
sorted_data4.rename(columns={'name': 'count'}, inplace=True)
sorted_data4 = sorted_data4.sort_values('count', ascending=False).reset_index()

# Задаем размер графика
plt.figure(figsize=(10, 7))

# Создаем график
graph4 = sns.barplot(x='category', y='count', hue='chain', data=sorted_data4)

# Поворачиваем подписи на 45 градусов
labels4 = graph4.get_xticklabels()
graph4.set_xticklabels(labels4, rotation=45)

# Убираем надпись 'chain' из легенды
graph4.legend(title=None)

# Добавляем заголовок и подписи осей
plt.title('Категории сетевых и несетевых заведений')
plt.xlabel('Категории')
plt.ylabel('Количество заведений')

# Отображаем график
plt.show()

Вывод

Сетевыми заведениями чаще являются кофейни, пицерии и булочные. У остальных категорий заведений чаще встречаются несетевые типы.

Сгруппируем данные по названиям заведений и найдем топ-15 популярных сетей в Москве (по количеству заведений)¶

In [20]:
# Подготовим данные для графика
sorted_data5 = data.query('chain==1')
sorted_data5 = sorted_data5.groupby(['name', 'category']).agg({'address': 'count'}).reset_index()
sorted_data5.rename(columns={'address': 'count'}, inplace=True)
sorted_data5 = sorted_data5.sort_values('count', ascending=False).reset_index().drop('index', axis=1)
sorted_data5 = sorted_data5.head(15)
sorted_data5
Out[20]:
name category count
0 Шоколадница кофейня 119
1 Домино'с Пицца пиццерия 76
2 Додо Пицца пиццерия 74
3 Яндекс.Лавка ресторан 72
4 One Price Coffee кофейня 71
5 Cofix кофейня 65
6 Prime ресторан 49
7 КОФЕПОРТ кофейня 42
8 Кулинарная лавка братьев Караваевых кафе 39
9 Теремок ресторан 36
10 CofeFest кофейня 31
11 Чайхана кафе 26
12 Буханка булочная 25
13 Drive Café кафе 24
14 Кофемания кофейня 22
In [21]:
# строим столбчатую диаграмму 
fig5 = px.bar(sorted_data5.sort_values(by='count', ascending=True), # загружаем данные и заново их сортируем
             x='count', # указываем столбец с данными для оси X
             y='name', # указываем столбец с данными для оси Y
             text='count', # добавляем аргумент, который отобразит текст с информацией о количестве
              hover_data=['category'] # добавляем аргумент, который отобразит категорию при наведении курсора
            )

# оформляем график
fig5.update_layout(title='ТОП-15 популярных сетей по количеству заведений',
                   xaxis_title='Количество заведений',
                   yaxis_title='Название сети')
fig5.show() # выводим график
In [22]:
print('ТОП-15 популярных сетей по количеству заведений относятся к следующим категориям:', sorted_data5['category'].unique())
ТОП-15 популярных сетей по количеству заведений относятся к следующим категориям: ['кофейня' 'пиццерия' 'ресторан' 'кафе' 'булочная']

Выводы

  • самой популярной сетью в Москве является "Шоколадница" (119 заведений)
  • следующими по популярности являются сети пиццерий "Домино'c Пицца" и "Додо Пицца" (76 и 74 заведений, соответственно)
  • обобщенно можно сказать, что самыми популярными заведениями являются кофейни, пиццерии и другие заведения с узнаваемым брендом
  • сети, которые вошли в ТОП-15, имеют своих постоянных клиентов и пользуются популярностью и в других регионах.

Проанализируем количество заведений в разных районах Москвы¶

In [23]:
# Подготовим данные для графика
sorted_data6 = data.groupby(['distr_short', 'category']).agg({'address': 'count'}).reset_index()
sorted_data6.rename(columns={'address': 'count'}, inplace=True)
sorted_data6 = sorted_data6.sort_values('count', ascending=False).reset_index().drop('index', axis=1)
sorted_data6_distr = sorted_data6.groupby(['distr_short']).agg({'count': 'sum'}).reset_index()
sorted_data6 = pd.merge(sorted_data6, sorted_data6_distr, on='distr_short')
sorted_data6.rename(columns={'count_x': 'count_cat', 'count_y': 'count_distr'}, inplace=True)
In [24]:
# Отсортируем данные
sorted_data6 = sorted_data6.sort_values(by=['count_distr', 'count_cat'], ascending=False)

# Создание столбчатой диаграммы
fig6 = px.bar(sorted_data6, x='distr_short', y='count_cat', color='category', labels={
    'category': 'Категория заведения',
    'distr_short': 'Административный район',
    'count_cat': 'Количество заведений'
})

# Настройка внешнего вида диаграммы
fig6.update_layout(title='Количество заведений по районам и категориям',
                  xaxis_title='Административный район',
                  yaxis_title='Количество заведений')

# Установка размера графика
fig6.update_layout(width=950, height=600)

fig6.show()
In [25]:
print('В датасете присутствуют', data['district'].nunique(), 'административных районов:', data['district'].unique())
В датасете присутствуют 9 административных районов: ['Северный административный округ'
 'Северо-Восточный административный округ'
 'Северо-Западный административный округ'
 'Западный административный округ' 'Центральный административный округ'
 'Восточный административный округ' 'Юго-Восточный административный округ'
 'Южный административный округ' 'Юго-Западный административный округ']

Выводы

  • больше всего заведений представлено в ЦАО Москвы
  • в ЦАО самой распространенной категорией являются рестораны, а в остальных районах - кафе.

Проанализируем средние (медианные) рейтинги в разных категориях заведений¶

In [26]:
# Определяем порядок сортировки
sorted_data7 = data.groupby('category').agg({'rating': 'median'}).sort_values('rating', ascending=False).reset_index()

# Задаем размер графика
plt.figure(figsize=(8, 6))

# Создаем график
graph7 = sns.barplot(x='category', y='rating', data=sorted_data7)

# Поворачиваем подписи на 45 градусов
labels7 = graph7.get_xticklabels()
graph7.set_xticklabels(labels7, rotation=45)

# Добавляем значения над каждым столбцом
for index, row in sorted_data7.iterrows():
    graph7.annotate('{:.1f}'.format(row['rating']), xy=(index, row['rating']), ha='center', va='bottom')

# ограничиваем ось Y для наглядности
plt.ylim(3.8, 4.5)
    
# Добавляем заголовок и подписи осей
plt.title('Медианные рейтинги заведений')
plt.xlabel('Категории заведений')
plt.ylabel('Медианный рейтинг')

# Отображаем график
plt.show()
In [27]:
# Задаем размер графика
plt.figure(figsize=(9, 8))

# применяем стиль darkgrid из библиотеки seaborn
sns.set_style('darkgrid')

# строим график boxplot средствами seaborn
sns.boxplot(x='rating', y='category', data=data)

# указываем заголовок графика и подписи осей средствами matplotlib
plt.title('Распределение средней оценки в зависимости категории заведений')
plt.xlabel('Средняя оценка')
plt.ylabel('Категории заведений')

# отображаем график на экране
plt.show()

Выводы

  • бары/пабы обычно получают самую высокую среднюю оценку - 4.4
  • в целом можно утверждать, что усредненные рейтинги в разных типах общепита не сильно отличаются и находятся в пределах от 4.2 до 4.4

Построим фоновую картограмму со средним (медианным) рейтингом заведений каждого района, отобразим все заведения датасета на карте¶

В датасете представлено 9 округов.

Для каждого округа посчитаем медианный рейтинг торговых центров, которые находятся на его территории.

In [28]:
rating_data = data.groupby('district', as_index=False)['rating'].agg('median').sort_values(by='rating', ascending=False)
rating_data
Out[28]:
district rating
5 Центральный административный округ 4.4
0 Восточный административный округ 4.3
1 Западный административный округ 4.3
2 Северный административный округ 4.3
4 Северо-Западный административный округ 4.3
7 Юго-Западный административный округ 4.3
8 Южный административный округ 4.3
3 Северо-Восточный административный округ 4.2
6 Юго-Восточный административный округ 4.2
In [29]:
# загружаем JSON-файл с границами округов Москвы

pth1 = '/datasets/admin_level_geomap.geojson'
pth2 = 'admin_level_geomap.geojson'

if os.path.exists(pth1):
    state_geo = pth1
elif os.path.exists(pth2):
    state_geo = pth2
else:
    print('Something is wrong')
In [30]:
# moscow_lat - широта центра Москвы, moscow_lng - долгота центра Москвы
moscow_lat, moscow_lng = 55.751244, 37.618423

# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)

# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
    geo_data=state_geo,
    data=rating_data,
    columns=['district', 'rating'],
    key_on='feature.name',
    fill_color='YlGn',
    fill_opacity=0.8,
    legend_name='Медианный рейтинг заведений по районам',
).add_to(m)

# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m)

# пишем функцию, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его в кластер marker_cluster
def create_clusters(row):
    Marker(
        [row['lat'], row['lng']],
        popup=f"{row['name']} {row['rating']}",
    ).add_to(marker_cluster)

# применяем функцию create_clusters() к каждой строке датафрейма
data.apply(create_clusters, axis=1)

# выводим карту
m
Out[30]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Проанализируем улицы Москвы по количеству заведений¶

Найдем топ-15 улиц по количеству заведений¶

In [31]:
sorted_data8 = data.groupby(['street', 'category']).agg({'address': 'count'}).reset_index()
sorted_data8.rename(columns={'address': 'count'}, inplace=True)
sorted_data8 = sorted_data8.sort_values('count', ascending=False).reset_index().drop('index', axis=1)
top_15_str = sorted_data8.groupby(['street']).agg({'count': 'sum'}).sort_values('count', ascending=False).reset_index().head(15)
top_15_str
Out[31]:
street count
0 проспект Мира 184
1 Профсоюзная улица 122
2 проспект Вернадского 108
3 Ленинский проспект 107
4 Ленинградский проспект 95
5 Дмитровское шоссе 88
6 Каширское шоссе 77
7 Варшавское шоссе 76
8 Ленинградское шоссе 70
9 МКАД 65
10 Люблинская улица 60
11 улица Вавилова 55
12 Кутузовский проспект 54
13 улица Миклухо-Маклая 49
14 Пятницкая улица 48

Построим график распределения количества заведений и их категорий по топ-15 улицам¶

In [32]:
# Подготовим данные для графика
sorted_data8 = sorted_data8[sorted_data8['street'].isin(top_15_str['street'])]
sorted_data8 = pd.merge(sorted_data8, top_15_str, on='street')
sorted_data8.rename(columns={'count_x': 'count_cat', 'count_y': 'count_str'}, inplace=True)
In [33]:
# Отсортируем данные
sorted_data8 = sorted_data8.sort_values(by=['count_str', 'count_cat'], ascending=False)

# Создание столбчатой диаграммы
fig8 = px.bar(sorted_data8, x='street', y='count_cat', color='category', labels={
    'category': 'Категория заведения',
    'street': 'Улица',
    'count_cat': 'Количество заведений'
})

# Настройка внешнего вида диаграммы
fig8.update_layout(title='ТОП-15 улиц по количеству заведений',
                  xaxis_title='Улица',
                  yaxis_title='Количество заведений')

fig8.show()

Выводы

  • больше всего заведений общепита расположено на проспекте Мира
  • во всех улицах кафе, рестораны и кофейни представляют значительную долю заведений общепита
  • выделяется МКАД: более половины всех заведений здесь работают в формате кафе, следующий по популярности - быстрое питание.

Найдем улицы, на которых находится только один объект общепита¶

In [34]:
# Выявляем улицы, где есть только 1 заведение
sorted_data9 = data.groupby(['street']).agg({'address': 'count'}).query('address==1').reset_index()
print('Всего улиц, на которых  находится только один объект общепита:', sorted_data9['street'].nunique())
print()
print(sorted_data9['street'].unique())
Всего улиц, на которых  находится только один объект общепита: 458

['1-й Автозаводский проезд' '1-й Балтийский переулок'
 '1-й Варшавский проезд' '1-й Вешняковский проезд'
 '1-й Голутвинский переулок' '1-й Грайвороновский проезд'
 '1-й Дербеневский переулок' '1-й Земельный переулок'
 '1-й Капотнинский проезд' '1-й Кирпичный переулок'
 '1-й Колобовский переулок' '1-й Котляковский переулок'
 '1-й Курьяновский проезд' '1-й Николощеповский переулок'
 '1-й Новокузнецкий переулок' '1-й Рижский переулок'
 '1-й Самотёчный переулок' '1-й Сетуньский проезд'
 '1-й Спасоналивковский переулок' '1-й Щипковский переулок'
 '1-я Боевская улица' '1-я Курьяновская улица' '1-я Парковая улица'
 '1-я Стекольная улица' '1-я Фрунзенская улица' '1-я Ямская улица'
 '1-я линия Хорошёвского Серебряного Бора' '11-я Парковая улица'
 '12-я Парковая улица' '14-я Парковая улица' '16-я Парковая улица'
 '17-й проезд Марьиной Рощи' '2-й Балтийский переулок'
 '2-й Боткинский проезд' '2-й Верхний Михайловский проезд'
 '2-й Вышеславцев переулок' '2-й Зачатьевский переулок'
 '2-й Кожуховский проезд' '2-й Красносельский переулок'
 '2-й Крестовский переулок' '2-й Лучевой просек'
 '2-й Новоподмосковный переулок' '2-й Обыденский переулок'
 '2-й Полевой переулок' '2-й Рощинский проезд' '2-й Силикатный проезд'
 '2-й Тверской-Ямской переулок' '2-й Хорошёвский проезд'
 '2-й Щемиловский переулок' '2-й квартал Капотни' '2-й квартал Капотня'
 '2-й переулок Петра Алексеева' '2-я Владимирская улица'
 '2-я Звенигородская улица' '2-я Карачаровская улица'
 '2-я Магистральная улица' '2-я Пугачёвская улица' '2-я Рощинская улица'
 '2-я Рыбинская улица' '2-я Фрезерная улица' '2-я улица Марьиной Рощи'
 '3-й Кадашёвский переулок' '3-й Лихачёвский переулок'
 '3-й Михалковский переулок' '3-й Новомихалковский проезд'
 '3-й Хорошёвский проезд' '3-й проезд Перова Поля'
 '3-й проезд Подбельского' '3-я Радиаторская улица'
 '3-я Тверская-Ямская улица' '3-я Фрунзенская улица'
 '4-й Воробьёвский проезд' '4-й Лесной переулок' '4-й Рощинский проезд'
 '4-я Кабельная улица' '4-я улица 8 Марта' '5-й проезд Подбельского'
 '5-я Магистральная улица' '5-я улица Ямского Поля' '6-й Лучевой просек'
 '6-я Парковая улица' '9-я улица Соколиной Горы' 'Алтайская улица'
 'Андреевский пешеходный мост' 'Андроньевская площадь' 'Анненский проезд'
 'Арбатский переулок' 'Архангельский переулок' 'Балакиревский переулок'
 'Барвихинская улица' 'Басманный тупик' 'Беговая аллея'
 'Бесединское шоссе' 'Бибиревская улица' 'Бобров переулок'
 'Богословский переулок' 'Большая Калитниковская улица'
 'Большая Пионерская улица' 'Большая Тихоновская улица'
 'Большой Волоколамский проезд' 'Большой Кисловский переулок'
 'Большой Козловский переулок' 'Большой Николопесковский переулок'
 'Большой Строченовский переулок' 'Большой Сухаревский переулок'
 'Большой Трёхгорный переулок' 'Борисовская улица' 'Боровское шоссе'
 'Ботанический переулок'
 'Ботанический сад Московского государственного университета'
 'Боярский переулок' 'Братеевский парк' 'Братиславский парк'
 'Брянская улица' 'Будайский проезд' 'Булатниковский проезд'
 'Бутиковский переулок' 'Вагоноремонтная улица' 'Валдайский проезд'
 'Васильевская улица' 'Верхнелихоборская улица'
 'Верхний Сусальный переулок' 'Верхняя улица' 'Верхоянская улица'
 'Весёлая улица' 'Воробьёвская набережная' 'Ворошиловский парк'
 'Выползов переулок' 'Гаврикова улица' 'Гагаринский переулок'
 'Гагаринский тоннель' 'Газгольдерная улица' 'Гжельский переулок'
 'Главный ботанический сад имени Н.В. Цицина Российской академии наук'
 'Глубокий переулок' 'Городецкая улица' 'Городская улица'
 'Гороховский переулок' 'Графский переулок' 'Грузинский сквер'
 'Гурьевский проезд' 'Даев переулок' 'Девяткин переулок'
 'Делегатская улица' 'Дендропарковая улица' 'Денежный переулок'
 'Дербеневская улица' 'Дмитровский переулок' 'Дорогобужская улица'
 'Дурасовский переулок' 'Ереванская улица' 'Живарёв переулок'
 'Жуков проезд' 'Загорьевский проезд' 'Задонский проезд'
 'Замоскворецкая линия' 'Заозёрная улица' 'Зарайская улица'
 'Звенигородская улица' 'Золотая улица' 'Зоологическая улица'
 'Зубарев переулок' 'Ивановская улица' 'Иваньковское шоссе'
 'Игарский проезд' 'Измайловская площадь' 'Инициативная улица'
 'Казарменный переулок' 'Каланчёвская улица' 'Каргопольская улица'
 'Клинская улица' 'Коломенская набережная' 'Колымажный переулок'
 'Кольцевая линия' 'Коммунистический переулок' 'Композиторская улица'
 'Конюшковская улица' 'Кооперативная улица'
 'Коптевский бульвар д 18 А стр 1' 'Костомаровский переулок'
 'Костромская улица' 'Котляковская улица' 'Красноармейская улица'
 'Красностуденческий проезд' 'Краснохолмская набережная'
 'Красноярская улица' 'Крылатский мост' 'Кубанская улица'
 'Кузьминская улица' 'Курсовой переулок' 'Кутузовский проезд'
 'Лазаревский переулок' 'Ленинский проспект (дублёр)'
 'Леонтьевский переулок' 'Лечебная улица'
 'Лианозовский парк культуры и отдыха' 'Лианозовский проезд'
 'Липецкая улица (дублёр)' 'Мажоров переулок' 'Малая Андроньевская улица'
 'Малая Красносельская улица' 'Малая Почтовая улица'
 'Малая Тульская улица' 'Маленковская улица'
 'Малый Гнездниковский переулок' 'Малый Дровяной переулок'
 'Малый Златоустинский переулок' 'Малый Ивановский переулок'
 'Малый Казённый переулок' 'Малый Кисловский переулок'
 'Малый Николопесковский переулок' 'Малый Патриарший переулок'
 'Малый Толмачёвский переулок' 'Мансуровский переулок' 'Медовый переулок'
 'Мерзляковский переулок' 'Миусская площадь' 'Михайловский проезд'
 'Молдавская улица' 'Москворецкая набережная' 'Московская улица'
 'Московский проспект' 'Мурановская улица' 'Мясницкий проезд'
 'Нащокинский переулок' 'Новая улица' 'Новгородская улица'
 'Новомосковская улица' 'Новопоселковая улица' 'Новороссийская улица'
 'Новосущёвская улица' 'Новощукинская улица' 'Новоясеневский тупик'
 'Одинцовская улица' 'Октябрьский переулок' 'Ордынский тупик'
 'Оренбургская улица' 'Орловский переулок' 'Оршанская улица'
 'Островная улица' 'Отрадный проезд' 'Павелецкая набережная'
 'Панкратьевский переулок' 'Парусный проезд' 'Певческий переулок'
 'Переведеновский переулок' 'Перекопская улица' 'Пермская улица'
 'Перовский парк культуры и отдыха' 'Пестовский переулок'
 'Петроверигский переулок' 'Петровско-Разумовская аллея'
 'Платовская улица' 'Пожарский переулок' 'Полоцкая улица'
 'Померанцев переулок' 'Поперечный просек' 'Посланников переулок'
 'Прибрежный проезд' 'Проектируемый проезд № 5265' 'Промышленный проезд'
 'Пронская улица' 'Просторная улица' 'Проточный переулок'
 'Пулковская улица' 'Путевой проезд' 'Пушкарёв переулок'
 'Раушская набережная' 'Савёловская линия' 'Самарская улица'
 'Самокатная улица' 'Светлогорский проезд' 'Северный бульвар'
 'Сеченовский переулок' 'Симферопольский проезд' 'Скатертный переулок'
 'Скотопрогонная улица' 'Смоленская-Сенная площадь' 'Советская улица'
 'Солянский тупик' 'Софийская набережная' 'Средний Тишинский переулок'
 'Ставропольская улица' 'Ставропольский проезд' 'Старокаширское шоссе'
 'Староконюшенный переулок' 'Старомонетный переулок'
 'Старый Петровско-Разумовский проезд' 'Старый Толмачёвский переулок'
 'Стахановская улица' 'Сторожевая улица' 'Стрелецкая улица'
 'Стромынский переулок' 'Ступинский проезд' 'Сумская' 'Сумской проезд'
 'Сытинский переулок' 'Сытинский тупик' 'Таганский парк культуры и отдыха'
 'Талдомская улица' 'Таможенный проезд' 'Тарутинская улица'
 'Таёжная улица' 'Терлецкий лесопарк' 'Товарищеский переулок'
 'Токмаков переулок' 'Третье транспортное кольцо' 'Триумфальная площадь'
 'Троилинский переулок' 'Трёхпрудный переулок' 'Турчанинов переулок'
 'Тучковская улица' 'Тюменский проезд' 'Угличская улица'
 'Уссурийская улица' 'Федеративный проспект' 'Хитровский переулок'
 'Хлыновский тупик' 'Хоромный тупик' 'Хохловский переулок'
 'Центральный парк культуры и отдыха имени М. Горького'
 'Чапаевский переулок' 'Челобитьевское шоссе' 'Челюскинская улица'
 'Чермянский проезд' 'Черёмушкинский проезд' 'Чонгарский бульвар'
 'Шоссейный проезд' 'Электрический переулок'
 'Юго-Западный административный округ' 'Якиманский переулок'
 'Яковоапостольский переулок' 'аллея Молодожёнов'
 'ландшафтный заказник Лианозовский' 'парк Алтуфьево'
 'парк Ангарские Пруды' 'парк Борисовские пруды'
 'парк Братеевская набережная' 'парк Дружбы' 'парк Зюзино'
 'парк Красная Пресня' 'парк Левобережный' 'парк Сад будущего'
 'парк Технических видов спорта' 'парк Тюфелева роща'
 'парк Этнографическая деревня Бибирево' 'парк имени Артёма Боровика'
 'парк искусств Музеон' 'переулок Капранова' 'площадь Журавлёва'
 'площадь Савёловского Вокзала' 'пр-т Комсомольский'
 'природно-исторический парк Измайлово' 'проезд Донелайтиса'
 'проезд Одоевского' 'проезд Ольминского' 'проезд Якушкина'
 'проспект Академика Сахарова' 'проспект Лихачёва' 'сад Эрмитаж'
 'сквер имени М.И. Калинина' 'ул. Профсоюзная' 'ул. Ярославская'
 'улица 8 Марта' 'улица 800-летия Москвы' 'улица Айвазовского'
 'улица Академика Ильюшина' 'улица Академика Комарова'
 'улица Антонова-Овсеенко' 'улица Артюхиной' 'улица Богородский Вал'
 'улица Большая Молчановка' 'улица Буженинова' 'улица Бусиновская Горка'
 'улица Васильцовский Стан' 'улица Ватутина' 'улица Вешних Вод'
 'улица Вильгельма Пика' 'улица Водников' 'улица Всеволода Вишневского'
 'улица Вучетича' 'улица Высоцкого' 'улица Габричевского' 'улица Гастелло'
 'улица Гашека' 'улица Генерала Глаголева' 'улица Генерала Дорохова'
 'улица Генерала Ермолова' 'улица Говорова' 'улица Годовикова'
 'улица Грекова' 'улица Губкина' 'улица Демьяна Бедного' 'улица Дудинка'
 'улица Зорге' 'улица Ивана Франко' 'улица Ильинка' 'улица Капотня'
 'улица Кашёнкин Луг' 'улица Кирпичные Выемки' 'улица Климашкина'
 'улица Клочкова' 'улица Комдива Орлова' 'улица Кондратюка'
 'улица Константина Федина' 'улица Конёнкова' 'улица Костякова'
 'улица Кутузова' 'улица Ленивка' 'улица Лефортовский Вал'
 'улица Литвина-Седого' 'улица Лобачика' 'улица Луиджи Лонго'
 'улица МЖД Киевское 5-й км' 'улица Максимова' 'улица Малая Полянка'
 'улица Малая Якиманка' 'улица Маршала Баграмяна'
 'улица Маршала Соколовского' 'улица Мусоргского' 'улица Неверовского'
 'улица Николая Химушина' 'улица Олеко Дундича' 'улица Острякова'
 'улица Паустовского' 'улица Петра Романова' 'улица Пивченкова'
 'улица Поликарпова' 'улица Полины Осипенко' 'улица Раевского'
 'улица Расковой' 'улица Расплетина' 'улица Ремизова' 'улица Речников'
 'улица Рогова' 'улица Розанова' 'улица Рокотова' 'улица Россолимо'
 'улица Садовники' 'улица Саляма Адиля' 'улица Самеда Вургуна'
 'улица Саморы Машела' 'улица Седова' 'улица Советской Армии'
 'улица Станиславского' 'улица Старые Кузьминки' 'улица Стасовой'
 'улица Усиевича' 'улица Уткина' 'улица Фадеева' 'улица Фотиевой'
 'улица Чечулина' 'улица Чистова' 'улица Шкулёва' 'улица Шкулёва 4'
 'улица Шухова' 'улица Юннатов' '№ 7']

Посмотрим, какие заведения расположены на таких улицах.

In [35]:
# Фильтруем таблицу data, чтобы оставить только улицы, где есть только 1 заведение
sorted_data10 = pd.merge(sorted_data9, data, on='street')

print('Всего', sorted_data10['name'].nunique(), 'заведений являются единственными заведениями на улице.')
print('----')
print('Из них', sorted_data10.query('chain==1')['name'].count(), 'являются сетевыми заведениями.')
print('----')
print('Список заведения, которые являются единственными заведениями на улице:')
print()
print(sorted_data10['name'].unique())
Всего 403 заведений являются единственными заведениями на улице.
----
Из них 133 являются сетевыми заведениями.
----
Список заведения, которые являются единственными заведениями на улице:

['Чайхана Азия' 'Хуан Хэ' 'Колизей' 'Deli by Shell' 'Shelby'
 'Домашняя кухня' 'Жаровня-офис' 'Арти' 'Халал' 'Вкус Life' 'Время чудес'
 'Вкусное' 'Чердачок' 'Ресторан Бабель' 'Dada' 'Как Дома' 'Кофе пью'
 'Сетунь' 'E. D. A.' 'БонАрхитект' 'Ставр' 'Тандыр' 'Две печеньки' 'Кафе'
 'Фреско' 'Кофейня 613' 'Сосны' 'Twice' 'Шаурма' 'Тихий дом' 'Сезоны'
 'Кафе домашняя еда' "Gump's" 'Le Круассан' 'Сущёвский двор'
 'Монастырская трапеза' 'Кафе-столовая' 'Монастырская чайная' 'Пирожковая'
 'Кафе Шашлычная 1957' 'Мадина' 'Борго' 'Spoon&Dagger' 'Sun Stek'
 'Ресторан Доктор на крыше' 'Кафе-столовая Техника питания' 'Самаргало'
 'Пекарня Буханка' 'Яндекс.Лавка' 'Burgers and Crabs' 'Дрова и Угли'
 'Farn House' 'Sub Cafe' 'Ресторан корейской кухни Новая Азия' 'Миль'
 'Столовая' 'Чайхана Vizir' 'Кофе On' 'Кафе 11/11' 'Грузинская Лавка'
 'CofeFest' 'Галерея вкуса' 'Тайм-ланч' 'Гелена' 'Pique-Nique'
 'Кулинарная лавка братьев Караваевых' 'Диемм' 'Лаки Сувлаки' 'Prime'
 'Магазин кафе' 'Формула за рулем' 'Ногбон осетинские пироги' 'City Lunch'
 'Дубки' 'Грузинская кухня' 'Хинкальная у Бехо' 'Пивной бар Pivo. Bar'
 'Кафе Пляж' 'Трапезная' 'Столовая в Марьиной роще' 'Single' 'Бахытик'
 'Air Coffee' 'Шаурма - Донер' 'Басманный парк' 'Chicken Mania' 'Теремок'
 'Жигулевское' 'Ex: Libris' 'Циники' 'Вителия' 'Портал' 'Кафетериус № 26'
 'Кондитерская-кулинария Брусника' 'La Veranda' 'Калитки' 'Вкус кофе'
 'White Fox' 'Мамина еда' 'Johnny Lee Pizza & Halal Food' 'Noba coffee'
 'Буфет ботанического сада' 'Well Doner' 'Сказка' 'Мирабель' 'Ланч Бокс'
 'Баам-кафе' 'Академия' 'Чайхана Баракат' 'Bollo' 'Шашлык.рф'
 'One Price Coffee' 'Угол' 'Self Cafe' 'Км халяль' 'Prawns' 'Star' 'Хаят'
 'Drive Café' 'Эларджи' 'Самса Халяль' 'Чайхана' 'Караван' 'Кофе'
 'Городское кафе 317' 'Чайхона №1' 'Мегобребо' 'Encore Cafe' '100ловая'
 'Grace pizza' 'Лига Шашлыков' 'Ланч поинт' 'Скрепка'
 'Итальянский ресторан DaPino' 'Пикник' 'Кантинетта Антинори' 'Салхино'
 'Seven' 'Блеск' 'Осетинские пироги Pir & Rog' 'Бистро' 'Ivoire café'
 'Тер. мос' 'Вкус Востока' 'ТЦ' 'Шашлычная' 'Зарайский дворик' 'Profitt'
 'Сити Лайф Ривер' 'Vegolife' 'Сторожка' 'First&Only' "Домино'с Пицца"
 'Шоколадница' 'Наш двор' 'Эль кафе' 'ВьетПлюс' 'Sova coffee'
 'Шаурма на районе № 1' 'Хазар' 'Кафе-трапезная Antiпa' 'Рукав'
 'Столовая-буфет' 'Corner cafe&kitchen' 'Zoo Beer & Grill' 'Шантимэль'
 'История' 'Алло! Пицца' 'Наша История' 'Naturality' 'Оливка'
 'Coffeewinner' 'Langet' 'Пеньки' 'Лайфхакер' 'Беседка' 'Кафе на Курсовом'
 'Имбирный карп' 'Культ Вкуса' 'FoodMoscowCoffee' 'Чехонте' 'Мохамед'
 'Testo мания' 'Тетри' 'Coffeplet' 'Мажор' 'Шафран' 'Bon Lavash'
 'Чайхана Зейтун' 'Ровесник' 'Кофейня на Дровах' 'Гастробар БеловЪ'
 'Ноев Ковчег' 'Тинто' 'Dobryakova Bakery' 'Амбарчик' 'From Berlin'
 'Приятного аппетита' 'Гастробар Cavina' 'Кофедей' 'Brera'
 'Лао Ли Менделеев' 'Глинтвейн' 'Фалафилло' 'Ням по пути' 'Мещерское'
 'Пиццаменто' 'Мастер вкуса' 'Барави' 'Александр' 'Аквафортуна'
 'Чай хана халал' 'Лагман Хаус' 'Ганга' 'Буфет' 'Кампа'
 'ABC Coffee Roasters' 'Шаурамус' 'КОФЕПОРТ' 'Kaya Coffee Shop'
 'Пармижано' 'St Cultura' 'Дружба' 'Легенда Самарканда'
 'Экспедиция. Северная кухня' 'Bonne Cantine' 'Оля' 'Трдельникъ'
 'Tasty Joys' 'Il Letterato' 'Итальянские каникулы' 'Фергана'
 '15 Kitchen+Bar' 'Пельменная' 'Korean Chick' '7/12' 'Линии вкуса'
 'Арт-кафе Сахар' 'Пекорино' 'Мазза' 'Бар, паб Проточный' 'Mr. Brown'
 'Кафе Пушкарёв' 'Санапиро' 'Чайхана Халаль 24' 'Fire Lake'
 '1901 Comfort Food Zone' 'Иду лесом' 'Энесай' 'Багратион'
 'Кухня & Маркет' 'BBCafe' 'Де Марко' 'Ресторан' 'Люди как люди'
 'Сакура Place' 'Актавест' 'Звездопад' 'Брусника' 'Baci Abbracci'
 'Мустафа Кебаб' 'Антилопа' 'Роза Ветров' 'ЧАЙХАНА 24/7' '9 Bar Coffee'
 'Достонжон' 'Eco шаурма' 'Кадастровая палата' 'Bowlroom'
 'Tilda Food & Bar' 'АндерСон' 'Шах-Даг' 'Ланч и Кофе' 'Мастера Тарутины'
 'Светлана' 'Method Beer & Munchies' 'Кафе Токмакоff' 'Кафе Чайковский'
 'Му-Му' 'Рыбный базар' 'Brasserie Lambic' 'Таверна Фили Хаус' 'Смакбери'
 "Coffeekaldi's" 'Шаверма' 'Восточная кухня' 'Сыр и кофе' 'Квартира 44'
 'Blanc' 'Пион' 'Сокол' 'Алазани' 'Guru' 'Антик' 'Мое кафе'
 'Практика Кофе' 'Удача' 'Семь хлебов' 'Drink Eat Travel' 'Кофепровод'
 'В самое сердце' 'Чебуречная история' 'Веранда' 'В парке вкуснее'
 'Cafe Vecher' 'Фастфуд' 'Пекарня' 'Cafe Racer' 'Di villaggio' 'Кафе-Кофе'
 'Пончики' 'Четвёртая стена' 'ZAcoffee' 'Чёрный кот'
 'French Bakery SeDelice' 'ФО БО ю' 'Сеть кофеен Латте' 'Донер Тандыр'
 'Хорошие знакомые' 'Фудмаркет' 'Бургер Бокс' "G'астроном" 'Coffeepie'
 "Jeffrey's Coffee" 'Сеть поминальных залов' 'Услада' 'Арамье' 'Стильавто'
 'Любить Кофе Пить' 'Ля Фантази' 'Чайхона' 'WTFcoffee' 'Завтрак на обед'
 'Траттория Evi' 'Дом обеда' 'Torsher' 'Марина' 'Кафетерий'
 'Съестная лавка' 'Шале-Прайд' 'Донер и гриль' 'Клуб Высоцкий'
 'E. S. Coffee' 'Альпини' 'Самолёт' 'Хинкальная' 'Кафе Москва' 'Кофе и'
 'Папа Карло' 'Хлеб с маслом' 'Шале-микс' 'Азербайджан' 'Coffee Itself'
 'Кафе Арбат' 'Тейст' 'Саят-Нова' 'Eat&Play' 'Чайная' 'Марио' 'Магнолия'
 'Cosmic Coffee' 'Mr. Пи' '1 Этаж' 'Фитнес кафе fresh fit' 'Пельмени'
 'Телиани' 'Хинкальный дворик' 'Палладиум' 'Гранат Black' 'Империя'
 'Онегинъ' 'William Bass' 'Ресторан Cafe de Arts' 'Green V. A. I.'
 'Ресторан-бар Crazy Brothers' 'Три жирафа' '10 Идеальных Пицц'
 'Кофейня Анклав' 'Plov Time' 'Буханка' 'Coffee Point'
 'БирХеванс ресторан бар' 'Остерия итальянцы' 'Речниковъ' 'По кайфу'
 'Coffeebrain' 'Кафе-гриль' 'Луар' 'Food Story' 'Кап кап'
 'Культура Вкусов' 'Бургер & Кофе' 'Лоза' 'Чебуреки' 'Ирина' 'Папа Джонс'
 'Гараж Гриль & Кофе Тайм' 'Просвет' 'Baza' 'Палермо, кафе' 'Мираж'
 'Scirocco' 'Енот']

Вывод

  • в основном единственными заведениями на улице являются менее узнаваемые заведения, которые не являются сетевыми.

Проанализируем примерную стоимость заказа в рублях и её изменение в зависимости от расположения завдения¶

Для каждого округа посчитаем медианное значение стоимости заказа в заведениях, которые находятся на его территории.

In [36]:
# Фильтруем только те строки, где у нас есть числовые значения для заказа
bill_data = data.query('middle_avg_bill != "N/A"')

# Переводим в числовой формат
bill_data['middle_avg_bill'] = pd.to_numeric(bill_data['middle_avg_bill'])

# Готовим данные для отражения на карте
bill_data = bill_data.groupby('district', as_index=False)['middle_avg_bill'].agg('median')\
            .sort_values(by='middle_avg_bill', ascending=False).reset_index().drop('index', axis=1)
bill_data
Out[36]:
district middle_avg_bill
0 Западный административный округ 1000.0
1 Центральный административный округ 1000.0
2 Северо-Западный административный округ 700.0
3 Северный административный округ 650.0
4 Юго-Западный административный округ 600.0
5 Восточный административный округ 575.0
6 Северо-Восточный административный округ 500.0
7 Южный административный округ 500.0
8 Юго-Восточный административный округ 450.0

Изобразим полученные значения на карте Москвы, построим фоновую картограмму.

In [37]:
# создаём карту Москвы
m2 = Map(location=[moscow_lat, moscow_lng], zoom_start=10)

# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
    geo_data=state_geo,
    data=bill_data,
    columns=['district', 'middle_avg_bill'],
    key_on='feature.name',
    fill_color='PuBu',
    fill_opacity=0.8,
    legend_name='Медианная стоимость заказа',
).add_to(m2)

# выводим карту
m2
Out[37]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Вывод

  • самое высокое значение среднего чека выявлено у ЦАО и ЗАО (где расположен деловой центр Москва-Сити)
  • при удалении от центра Москвы средний чек заведений снижается
  • на западе Москвы средний чек у заведений общепита выше, чем на востоке.

Общий вывод по разделу¶

  • В Москве представлено больше всего заведений общественного питания в формате кафе и ресторанов, меньше всего - столовых и булочных.
  • Усредненные рейтинги в разных типах общепита не сильно отличаются и находятся в пределах от 4.2 до 4.4.
  • В Москве 38% всех заведений являются сетевыми. Кафе и ресторан обычно не относятся к какой-либо сети (доля сетевых меньше 40%), а вот у кофеен, пиццерий и булочны сетевые и несетевые заведения представлены почти в одинаковом количестве.
  • Самыми крупными заведениями по количеству посадочных мест обычно являются рестораны, самыми маленькими - булочные.
  • Самой популярной сетью по количеству заведений является "Шоколадница", следующими по популярности являются сети пиццерий "Домино'c Пицца" и "Додо Пицца". Сети, которые вошли ТОП-15 по количеству заведений, обычно являются кофейнями, пиццериями или другие заведения с узнаваемым брендом. Эти заведения имеют своих постоянных клиентов и пользуются популярностью и в других регионах.
  • В 458 улицах Москвы расположено всего 1 заведение. В основном единственными заведениями на улице являются менее узнаваемые заведения, которые не являются сетевыми. Улицей, где расположено больше всего заведений общепита является проспект Мира.
  • Среди районов Москвы выделяетсы ЦАО: здесь представлено больше всего заведений общественного питания, также здесь самый высокий средний чек и самый высокий средний рейтинг заведений. В ЦАО самой распространенной категорией являются рестораны, а в остальных районах - кафе.
  • При удалении от центра Москвы средний чек заведений снижается, а на западе Москвы средний чек у заведений общепита выше, чем на востоке. В ЗАО средний чек сопоставим со средним чеком ЦАО. Это, скорее всего связано с тем, что ЗАО также является деловым центром (там расположен Москва-Сити).

Детализируем исследование: открытие кофейни¶

Основателям фонда «Shut Up and Take My Money» не даёт покоя успех сериала «Друзья». Их мечта — открыть такую же крутую и доступную, как «Central Perk», кофейню в Москве. Заказчики не боятся конкуренции в этой сфере, ведь кофеен в больших городах уже достаточно. Попробуем проанализировать, осуществима ли эта мечта.

Особенности расположения¶

Кофейню потенциально можно расположить в двух местах: 1) на улице, где отсутствуют другие кофейни, и конкуренция

2) на улице, где существует большая концентрация кофеен, и, соответственно,потребители специально туда едут за кофе из-за наличия большого выбора (при этом надо учитывать, чтобы на этой улице отсутствовали сетевые кофейни, так как их потребители будут лояльны бренду)

Для начала посмотрим, как количество кофеен меняется в зависимости от района.

In [38]:
# Подготовим данные для графика
sorted_data11 = sorted_data6.query('category=="кофейня"').sort_values(by='count_cat', ascending=False)

# Создание столбчатой диаграммы
fig11 = go.Figure()

# Добавление столбцов
fig11.add_trace(go.Bar(x=sorted_data11['distr_short'][:-1], y=sorted_data11['count_cat'][:-1],
                        marker=dict(color='rgba(0, 0, 255, 1)'), # Можете выбрать свой цвет для столбцов
                       hovertemplate='<br>'.join([
                            'Административный район: %{x}',
                            'Количество кофеен: %{y}'
                        ])
                       ))
fig11.add_trace(go.Bar(x=sorted_data11['distr_short'][-1:], y=sorted_data11['count_cat'][-1:],
                        marker=dict(color='rgba(255, 0, 0, 1.0)'), # Можете выбрать свой цвет для последнего столбца
                       hovertemplate='<br>'.join([
                            'Административный район: %{x}',
                            'Количество кофеен: %{y}'
                        ])
                        ))

# Настройка внешнего вида диаграммы
fig11.update_layout(
    title={
        'text': 'Количество кофеен в районах Москвы',
        'font': {'size': 20}
    },
    xaxis_title={
        'text': 'Административный район',
        'font': {'size': 16}
    },
    yaxis_title={
        'text': 'Количество кофеен',
        'font': {'size': 16}
    },
    font=dict(
        size=15
    ), showlegend=False)
                   

# Установка размера графика
fig11.update_layout(width=950, height=600)

fig11.show()

Вывод

  • меньше всего кофеен расположено в СЗАО, кофейню лучше открыть там.

Выделим улицы СЗАО, где отсутствуют другие кофейни в новый датафрейм data_streets_0¶

Выделим те улицы СЗАО, где отсутствуют другие кофейни, и конкуренция.

In [39]:
# группируем данные по улицам и категориям, считаем количество заведений
sorted_data12 = data.groupby(['street', 'category']).agg({'address': 'count'}).reset_index()

# фильтруем улицы, на которых нет категории "кофейня"
sorted_data12 = sorted_data12.groupby('street').filter(lambda x: 'кофейня' not in x['category'].values)['street'].unique()

# создаем таблицу с информацией об улицах СЗАО , где отсутствует кофейня
data_streets_0 = data[data['street'].isin(sorted_data12)].reset_index().drop('index', axis=1)
data_streets_0 = data_streets_0.query('distr_short=="СЗАО"').reset_index().drop('index', axis=1)
data_streets_0['street'].unique()
Out[39]:
array(['Парусный проезд', 'Походный проезд', 'Лодочная улица',
       'улица Мещерякова', 'бульвар Яна Райниса', 'улица Фомичёвой',
       'улица Фабрициуса', 'улица Василия Петушкова',
       'улица Вилиса Лациса', 'Строительный проезд', 'проезд Донелайтиса',
       'Новопоселковая улица', 'Таллинская улица', 'улица Твардовского',
       'Одинцовская улица', 'улица Исаковского',
       'памятник природы Серебряный бор',
       '4-я линия Хорошёвского Серебряного Бора', 'улица Маршала Рыбалко',
       'улица Максимова', 'улица Маршала Соколовского',
       'улица Маршала Тухачевского', 'улица Расплетина',
       'улица Академика Бочвара', 'Живописная улица',
       'Иваньковское шоссе', '1-я линия Хорошёвского Серебряного Бора',
       'Большой Волоколамский проезд', 'улица Паршина',
       'Новохорошёвский проезд', 'улица Маршала Новикова',
       'улица Генерала Глаголева', 'улица Водников', 'улица Рогова',
       'улица Демьяна Бедного', 'улица Саляма Адиля',
       '1-й Силикатный проезд', 'Причальный проезд',
       '2-й Силикатный проезд'], dtype=object)

Выделим улицы СЗАО с большой концентрацией несетевых кофеен (2 и более) в новый датафрейм data_streets_1¶

In [40]:
# фильтруем данные и группуируем по улице
sorted_data18 = data.query('category=="кофейня" and distr_short=="СЗАО" and chain==0')\
        .groupby(['street']).agg({'address': 'count'}).reset_index()

# оставляем названия улицы СЗАО с большой концентрацией несетевых кофеен (2 и более)
sorted_data18 = sorted_data18.query('address>=2').reset_index().drop('index', axis=1)['street'].unique()

# создаем новый датафрейм со всеми данными из data, где будут улицы СЗАО с большой концентрацией несетевых кофеен
data_streets_1 = data[data['street'].isin(sorted_data18)].reset_index().drop('index', axis=1)
data_streets_1 = data_streets_1.query('category=="кофейня" and chain==0').reset_index().drop('index', axis=1)

print('улицы СЗАО с большой концентрацией несетевых кофеен (2 и более):')
print()
print(data_streets_1['street'].unique())
улицы СЗАО с большой концентрацией несетевых кофеен (2 и более):

['Сходненская улица' 'улица Маршала Катукова' 'бульвар Генерала Карбышева'
 'Шелепихинская набережная']
In [41]:
# Подготовим данные для графика
sorted_data17 = data_streets_1.groupby(['street']).agg({'address': 'count'}).reset_index()

# Отсортируем данные
sorted_data17 = sorted_data17.sort_values(by=['address'], ascending=True)

# Создание столбчатой диаграммы
fig17 = px.bar(sorted_data17, y='street', x='address', labels={
    'street': 'Улица',
    'address': 'Количество кофеен'
})

# Настройка внешнего вида диаграммы
fig17.update_layout(title={
        'text': 'Улицы СЗАО с большой концентрацией несетевых кофеен',
        'font': {'size': 20}
    },
    xaxis_title={
        'text': 'Количество кофеен',
        'font': {'size': 16}
    },
    yaxis_title={
        'text': 'Улица',
        'font': {'size': 16}
    })

fig17.update_xaxes(tickfont=dict(size=14), dtick=1)
fig17.update_yaxes(tickfont=dict(size=14))

fig17.show()

Вывод

Мы выделили улицы СЗАО, где лучше расположить кофейню. Проанализируем другие критерии, чтобы сузить выборку улиц.

Время работы¶

Проанализируем, много ли в Москве круглосуточных кофеен.

In [42]:
# готовим данные для графика
sorted_data13 = data.query('category=="кофейня"')['is_24_7'].value_counts().sort_values(ascending=False).reset_index()
sorted_data13.columns = ['is_24_7', 'count']
sorted_data13
Out[42]:
is_24_7 count
0 False 1354
1 True 59
In [43]:
# готовим данные для графика - переименуем булевые значения
sorted_data13['is_24_7'] = sorted_data13['is_24_7'].replace([False, True], ['не круглосуточные', 'круглосуточные'])


# строим круговую диаграмму
fig13 = go.Figure(data=[go.Pie(labels=sorted_data13['is_24_7'], # указываем значения, которые появятся на метках сегментов
                             values=sorted_data13['count'], # указываем данные, которые отобразятся на графике
                             pull = [0.1, 0])]) # добавляем аргумент, который выделит тип-лидер на графике
fig13.update_layout(title='Соотношение круглосуточных и не круглосуточных кофеен', # указываем заголовок графика
                  width=830, # указываем размеры графика
                  height=500,
                  annotations=[dict(x=1.12, # вручную настраиваем аннотацию легенды
                                    y=1.05,
                                    text='Количество кофеен',
                                    showarrow=False)],
                   font=dict(size=13))
fig13.show() # выводим график

Вывод

  • в Москве доля круглосуточных кофеен крайне низкая - 4.18%; скорее всего, это означает, что при прочих равных держать круглосуточную кофейню не выгодно (круглосуточными лучше делать бары/пабы)

  • делать круглосуточные кафе имеет смысл только чтобы заработать конкурентное преимущество в местах с большой концентрацией кофеен.

Проверим data_streets_1 на наличие круглосуточных кофеен¶

In [44]:
data_streets_1.query('is_24_7==True')
Out[44]:
name category address district hours lat lng rating price avg_bill middle_avg_bill middle_coffee_cup chain seats street is_24_7 distr_short

Вывод

  • в улицах СЗАО с большой концентрацией несетевых кофеен отсутствуют круглосуточные кофейни.

Рейтинги¶

Посмотрим, как рейтинг кофеен меняются в зависимости от района.

In [45]:
# Определяем порядок сортировки
sorted_data14 = data.query('category=="кофейня"').groupby('distr_short').agg({'rating': 'median'}).sort_values('rating', ascending=False).reset_index()

# Задаем размер графика
plt.figure(figsize=(8, 6))

# Создаем график
graph14 = sns.barplot(x='distr_short', y='rating', data=sorted_data14)

# Поворачиваем подписи на 45 градусов
labels14 = graph14.get_xticklabels()
graph14.set_xticklabels(labels14, rotation=45)

# Добавляем значения над каждым столбцом
for index, row in sorted_data14.iterrows():
    graph14.annotate('{:.1f}'.format(row['rating']), xy=(index, row['rating']), ha='center', va='bottom')

# ограничиваем ось Y для наглядности
plt.ylim(4, 4.4)
    
# Добавляем заголовок и подписи осей
plt.title('Медианные рейтинги кофеен')
plt.xlabel('Административные районы')
plt.ylabel('Медианный рейтинг')

# Отображаем график
plt.show()

Вывод

  • медианные рейтинги кофеен не отличаются между районами Москвы.

Проанализируем средние рейтинги в data_streets_1 (улицы с большой концентрацией несетевых кофеен)¶

In [46]:
# определяем порядок сортировки
sorted_data15 = data_streets_1.groupby('street').agg({'rating': 'median'}).sort_values('rating', ascending=False).reset_index()
sorted_data15['rating'] = sorted_data15['rating'].apply(lambda x: round(x, 2))

# строим столбчатую диаграмму 
fig15 = px.bar(sorted_data15.sort_values(by='rating', ascending=True), # загружаем данные и заново их сортируем
             x='rating', # указываем столбец с данными для оси X
             y='street', # указываем столбец с данными для оси Y
             text='rating', # добавляем аргумент, который отобразит текст с информацией о количестве
             hover_data=['rating'] # добавляем аргумент, который отобразит категорию при наведении курсора
            )

# оформляем график
fig15.update_layout(title='Рейтинги кофеен на улицах с большой концентрацией несетевых кофеен',
                   xaxis_title='Медианный рейтинг',
                   yaxis_title='Улица',
                   font=dict(size=13)) # увеличиваем размер шрифта

# красим нижний столбец в красный
fig15.update_traces(marker=dict(color=['red' if x == sorted_data15['rating'].max() else 'blue' for x in sorted_data15['rating']]))

fig15.update_xaxes(range=[3.6, 4.8])
fig15.show() # выводим график

Вывод

На улице Маршала Катукова медианный пользовательский рейтинг кофеен самый низкий среди ТОП-4 улиц СЗАО по концентрации несетевых кофеен. На этой улице есть возможность открыть кафе и навязать конкуренцию.

Проанализируем средние рейтинги в data_streets_0 (улицы, где отсутствуют кофейни)¶

Так как мы до этого выявили много улиц СЗАО, где отсутствуют кофейни, сразу поставим фильтр на средний рейтинг заведений меньше 4 и покажем оставшиеся улицы на графике.

In [47]:
# определяем порядок сортировки
sorted_data16 = data_streets_0.groupby('street').agg({'rating': 'median'}).query('rating<4').sort_values('rating', ascending=False).reset_index()

# ручная корректировка самого крупного значения, чтобы максимум был только один (нужно, чтобы удалось закрасить только один элемент)
sorted_data16.loc[0, 'rating'] = sorted_data16.loc[0, 'rating']+0.01
sorted_data16['rating'] = sorted_data16['rating'].apply(lambda x: round(x, 2))


# строим столбчатую диаграмму 
fig16 = px.bar(sorted_data16.sort_values(by='rating', ascending=True), # загружаем данные и заново их сортируем
             x='rating', # указываем столбец с данными для оси X
             y='street', # указываем столбец с данными для оси Y
             text='rating', # добавляем аргумент, который отобразит текст с информацией о количестве
             hover_data=['rating'] # добавляем аргумент, который отобразит категорию при наведении курсора
            )

# оформляем график
fig16.update_layout(title='Медианные рейтинги заведений на улицах без кофеен',
                   xaxis_title='Медианный рейтинг',
                   yaxis_title='Улица',
                    font=dict(size=13)) # увеличиваем размер шрифта

# красим нижний столбец в красный
fig16.update_traces(marker=dict(color=['red' if x == sorted_data16['rating'].max() else 'blue' for x in sorted_data16['rating']]))

fig16.update_xaxes(range=[2, 4])
fig16.show() # выводим график

Вывод

В Новохорошевском проезде отсутствуют кофейни, а заведения общепита, которые там есть, получили низкий рейтинг от посетителей. Следовательно, кофейню лучше открыть там.

Цены на капучино¶

Проанализируем медианную стоимость чашки капучино в Москве: для каждого округа посчитаем медианное значение стоимости капучино в кофейнях, которые находятся на его территории.

In [48]:
# Фильтруем только те строки, где у нас есть числовые значения для заказа
cof_data = data.query('middle_coffee_cup != "N/A" and category=="кофейня"')

# Переводим в числовой формат
cof_data['middle_coffee_cup'] = pd.to_numeric(cof_data['middle_coffee_cup'])

# Готовим данные для отражения на карте
cof_data = cof_data.groupby('district', as_index=False)['middle_coffee_cup'].agg('median')\
            .sort_values(by='middle_coffee_cup', ascending=False).reset_index().drop('index', axis=1)
cof_data
Out[48]:
district middle_coffee_cup
0 Юго-Западный административный округ 198.0
1 Центральный административный округ 190.0
2 Западный административный округ 189.0
3 Северо-Западный административный округ 165.0
4 Северо-Восточный административный округ 162.5
5 Северный административный округ 159.0
6 Южный административный округ 150.0
7 Юго-Восточный административный округ 147.5
8 Восточный административный округ 135.0

Изобразим полученные значения на карте Москвы, построим фоновую картограмму.

In [49]:
# создаём карту Москвы
m3 = Map(location=[moscow_lat, moscow_lng], zoom_start=10)

# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
    geo_data=state_geo,
    data=cof_data,
    columns=['district', 'middle_coffee_cup'],
    key_on='feature.name',
    fill_color='Oranges',
    fill_opacity=0.8,
    legend_name='Медианная стоимость капучино'
).add_to(m3)

# выводим карту
m3
Out[49]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Вывод

Цена на капучино заметно отличается в зависимости от района Москвы. Так как кофейня будет расположена в СЗАО, при открытии стоит ориентироваться на стоимость равную 165 руб.

Общий вывод по разделу¶

Рекомендуется открыть кофейню в СЗАО на Новохорошевском проезде или на улице Маршала Катукова.

  • На улице Маршала Чуйкова большая концентрация несетевых кофеен и, следовательно, большой поток посетителей. Кофейню можно сделать круглосуточным для достижения конкурентного преимущества.
  • На Новохорошевском проезде, наоборот, отсутствуют кофейни, а имеющиеся заведения общепита имеют низкий рейтинг у посетителей.

При открытии кофейни стоит ориентироваться на медианную стоимость капучино в СЗАО - 165 руб.

Презентация: https://disk.yandex.ru/i/dJzZAFEZeNBPHw